package indi.h.security.encryption.sym;

import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import indi.h.security.Parameters;
import indi.h.security.encryption.AbstractCrypt;
import indi.h.security.encryption.CryptFactoryConfig;
import indi.h.security.util.RandomData;

public class SymFactoryConfig<T extends Enum<?>> implements CryptFactoryConfig<T> {
	protected static Map<Enum<?>, Integer> defaultLength;
	protected static Map<Enum<?>, String> defaultCipher;
	protected Enum<?> e;
	static {
		defaultLength = new HashMap<Enum<?>, Integer>();
		defaultCipher = new HashMap<Enum<?>, String>();
		/**
		 * base crypt
		 */
		defaultLength.put(SymCryptType.DES, 56);
		defaultLength.put(SymCryptType.IDEA, 128);
		defaultLength.put(SymCryptType.AES, 128);
		defaultLength.put(SymCryptType.Blowfish, 32);
		defaultLength.put(SymCryptType.ARCFOUR, 40);
		defaultLength.put(SymCryptType.RC2, 32);
		defaultLength.put(SymCryptType.DESede, 112);
		defaultLength.put(SymCryptType.TripleDES, 112);
		defaultLength.put(SymCryptType.Camellia, 128);// key bytes length must
														// equal 16/24/32
		defaultLength.put(SymCryptType.CAST5, 32);
		defaultLength.put(SymCryptType.CAST6, 64);
		defaultLength.put(SymCryptType.GOST28147, 256);// key bytes needs to be
														// 32 byte - 256 bit
		defaultLength.put(SymCryptType.Noekeon, 128);
		defaultLength.put(SymCryptType.RC5, 128);
		defaultLength.put(SymCryptType.RC4, 64);
		defaultLength.put(SymCryptType.RC6, 256);
		defaultLength.put(SymCryptType.Rijndael, 128);
		defaultLength.put(SymCryptType.DSTU7624, 128);// unsupported key length:
														// only 128/256/512 are
														// allowed
		defaultLength.put(SymCryptType.SEED, 128);
		defaultLength.put(SymCryptType.Serpent, 32);
		defaultLength.put(SymCryptType.Shacal2, 128);// Shacal2-key must be 16 -
														// 64 bytes and multiple
														// of 8
		defaultLength.put(SymCryptType.Skipjack, 128);
		defaultLength.put(SymCryptType.SM4, 128);// SM4 requires a 128 bit key
		defaultLength.put(SymCryptType.TEA, 128);// Key size must be 128 bits.
		defaultLength.put(SymCryptType.Twofish, 64);// Key size between 64-256
													// bits
		defaultLength.put(SymCryptType.XTEA, 128);// Key size must be 128 bits.
		/**
		 * no supporting
		 */
		defaultLength.put(SymCryptType.Threefish, 64);// Cannot find any
														// provider supporting
														// Threefish-----no
		defaultLength.put(SymCryptType.VMPCKSA3, 64);// Cannot find any provider
														// supporting
														// VMPCKSA3------no
		defaultLength.put(SymCryptType.OpenSSLPBKDF, 256);// Cannot find any
															// provider
															// supporting
															// OpenSSLPBKDF----no
		/**
		 * need iv key
		 */
		defaultLength.put(IVCryptType.XSalsa20, 256);// XSalsa20 requires a 256
														// bit key and need iv
														// param
		defaultLength.put(IVCryptType.ChaCha, 128);// ChaCha20 requires 128 bit
													// or 256 bit key and need
													// iv param
		defaultLength.put(IVCryptType.HC128, 128);// The key must be 128 bits
													// long and need iv param
		defaultLength.put(IVCryptType.HC256, 256);// The key must be 128/256
													// bits long and need iv
													// param
		defaultLength.put(IVCryptType.Salsa20, 128);// Salsa20 requires 128 bit
													// or 256 bit key and need
													// iv param
		defaultLength.put(IVCryptType.VMPC, 32);// need iv param
		defaultLength.put(IVCryptType.Grainv1, 128);// key size must > 128,need
													// iv param
		defaultLength.put(IVCryptType.Grain128, 128);// key size must > 128,need
														// iv param
		/**
		 * pbe
		 */
		defaultLength.put(PBECryptType.PBEWithMD5AndTripleDES, 100);
		defaultLength.put(PBECryptType.PBEWithMD5AndDES, 100);
		defaultLength.put(PBECryptType.PBEWithSHA1AndDESede, 100);
		defaultLength.put(PBECryptType.PBEWithSHA1AndRC2_40, 100);
		defaultLength.put(PBECryptType.PBEWithSHA1AndRC2_128, 100);
		defaultLength.put(PBECryptType.PBEWithSHA1AndRC4_40, 100);
		defaultLength.put(PBECryptType.PBEWithSHA1AndRC4_128, 100);
		defaultLength.put(PBECryptType.PBEWithHmacSHA1AndAES_128, 100);
		defaultLength.put(PBECryptType.PBEWithHmacSHA512AndAES_256, 100);

		// ----------------------------------------------
		/**
		 * base crypt
		 */
		defaultCipher.put(SymCryptType.DES, "DES");
		defaultCipher.put(SymCryptType.IDEA, "IDEA");
		defaultCipher.put(SymCryptType.AES, "AES");
		defaultCipher.put(SymCryptType.Blowfish, "Blowfish");
		defaultCipher.put(SymCryptType.ARCFOUR, "ARCFOUR");
		defaultCipher.put(SymCryptType.RC2, "RC2");
		defaultCipher.put(SymCryptType.DESede, "DESede");
		defaultCipher.put(SymCryptType.TripleDES, "TripleDES");
		defaultCipher.put(SymCryptType.Camellia, "Camellia");
		defaultCipher.put(SymCryptType.CAST5, "CAST5");
		defaultCipher.put(SymCryptType.CAST6, "CAST6");
		defaultCipher.put(SymCryptType.GOST28147, "GOST28147");
		defaultCipher.put(SymCryptType.Noekeon, "Noekeon");
		defaultCipher.put(SymCryptType.RC5, "RC5");
		defaultCipher.put(SymCryptType.RC4, "RC4");
		defaultCipher.put(SymCryptType.RC6, "RC6");
		defaultCipher.put(SymCryptType.Rijndael, "Rijndael");
		defaultCipher.put(SymCryptType.DSTU7624, "DSTU7624");
		defaultCipher.put(SymCryptType.SEED, "SEED");
		defaultCipher.put(SymCryptType.Serpent, "Serpent");
		defaultCipher.put(SymCryptType.Shacal2, "Shacal2");
		defaultCipher.put(SymCryptType.Skipjack, "Skipjack");
		defaultCipher.put(SymCryptType.SM4, "SM4");
		defaultCipher.put(SymCryptType.TEA, "TEA");
		defaultCipher.put(SymCryptType.Twofish, "Twofish");
		defaultCipher.put(SymCryptType.XTEA, "XTEA");
		/**
		 * no supporting
		 */
		defaultCipher.put(SymCryptType.Threefish, "Threefish");// Cannot find
																// any
		// provider supporting
		// Threefish-----no
		defaultCipher.put(SymCryptType.VMPCKSA3, "VMPCKSA3");// Cannot find any
																// provider
		// supporting
		// VMPCKSA3------no
		defaultCipher.put(SymCryptType.OpenSSLPBKDF, "OpenSSLPBKDF");// Cannot
																		// find
																		// any
		// provider
		// supporting
		// OpenSSLPBKDF----no
		/**
		 * need iv key
		 */
		defaultCipher.put(IVCryptType.XSalsa20, "XSalsa20");
		defaultCipher.put(IVCryptType.ChaCha, "ChaCha");
		defaultCipher.put(IVCryptType.HC128, "HC128");
		defaultCipher.put(IVCryptType.HC256, "HC256");
		defaultCipher.put(IVCryptType.Salsa20, "Salsa20");
		defaultCipher.put(IVCryptType.VMPC, "VMPC");
		defaultCipher.put(IVCryptType.Grainv1, "Grainv1");
		defaultCipher.put(IVCryptType.Grain128, "Grain128");
		/**
		 * pbe
		 */
		defaultCipher.put(PBECryptType.PBEWithMD5AndTripleDES, "PBEWithMD5AndTripleDES");
		defaultCipher.put(PBECryptType.PBEWithMD5AndDES, "PBEWithMD5AndDES");
		defaultCipher.put(PBECryptType.PBEWithSHA1AndDESede, "PBEWithSHA1AndDESede");
		defaultCipher.put(PBECryptType.PBEWithSHA1AndRC2_40, "PBEWithSHA1AndRC2_40");
		defaultCipher.put(PBECryptType.PBEWithSHA1AndRC2_128, "PBEWithSHA1AndRC2_128");
		defaultCipher.put(PBECryptType.PBEWithSHA1AndRC4_40, "PBEWithSHA1AndRC4_40");
		defaultCipher.put(PBECryptType.PBEWithSHA1AndRC4_128, "PBEWithSHA1AndRC4_128");
		defaultCipher.put(PBECryptType.PBEWithHmacSHA1AndAES_128, "PBEWithHmacSHA1AndAES_128");
		defaultCipher.put(PBECryptType.PBEWithHmacSHA512AndAES_256, "PBEWithHmacSHA512AndAES_256");
		//--------------------------------
		defaultLength.put(SymCryptType.P_2_16_840_1_101_3_4_2, 128);
		defaultCipher.put(SymCryptType.P_2_16_840_1_101_3_4_2, "2.16.840.1.101.3.4.2");
		defaultLength.put(SymCryptType.ARC4, 128);
		defaultCipher.put(SymCryptType.ARC4, "ARC4");
		defaultLength.put(SymCryptType.ARIA, 128);
		defaultCipher.put(SymCryptType.ARIA, "ARIA");
		defaultLength.put(SymCryptType.TNEPRES, 128);
		defaultCipher.put(SymCryptType.TNEPRES, "TNEPRES");
	}

	@Override
	public Parameters getParameters() {
		// TODO Auto-generated method stub
		if (e instanceof PBECryptType) {
			PBECryptParameter pbecp = (PBECryptParameter) initDefaultParam(new PBECryptParameter());
			pbecp.setSalt(RandomData.randomByte(8));
			return pbecp;
		}
		if (e instanceof SymCryptType)
			return initDefaultParam(new SymCryptParameter());
		if (e instanceof IVCryptType)
			return initDefaultParam(new IVCryptParameter());
		return null;
	}

	@Override
	public AbstractCrypt newCrypt() {
		// TODO Auto-generated method stub
		if (e instanceof PBECryptType)
			return new PBECryptImp();
		if (e instanceof SymCryptType)
			return new SymCryptImp();
		if (e instanceof IVCryptType)
			return new IVCryptImp();
		return null;
	}

	protected SymCryptParameter initDefaultParam(SymCryptParameter s) {
		s.setPassword(RandomData.randomStr(8));
		s.setCode(Charset.forName("utf-8"));
		s.setAlgorithm(algorithmHandle());
		s.setCipherAlgorithm(defaultCipher.get(e));
		s.setLength(defaultLength.get(e));
		return s;
	}

	protected String algorithmHandle() {
		String eV = e.toString();
		if (eV.startsWith("P_")) {
			return eV.substring(2).replace("_", ".");
		}
		return eV;
	}

	@Override
	public void init(T e) {
		this.e = e;
	}

}
